Transactional memory is a mechanism that allows concurrent programs to be written more simply. A transaction specifies a sequence of code that is supposed to execute “as if” it were executing in isolation. In practice, transactions are allowed to execute concurrency, and conflicting data accesses are then detected. Decisions on what to do when contention occurs, i.e., “contention management,” can have large effects on the performance of transaction systems.
There is an important underlying problem in implementing a contention manager, however. Suppose that transaction Tx1 detects contention with transaction Tx2—perhaps Tx2 holds a pessimistic write lock on a data item that Tx1 also wishes to lock. In some situations, it might make sense for a contention manager to abort Tx2, allowing Tx1 to acquire the lock. Perhaps Tx2 is short, and Tx1 is long, so the cost of redoing Tx2's work after abort would be much less than redoing Tx1's.
But in this example scenario, Tx1 noticed the contention, and thus seems like the logical place to execute the logic of the contention management decision. But to do so, it needs information about Tx2, such as statistics about its execution, the contents of its transaction log, or perhaps to request that Tx2 voluntarily aborts, freeing up its acquired locks. This information most naturally resides in the data structure representing the transaction Tx2. For efficiency reasons, it is desirable to have this data structure be local to the thread executing Tx2, rather than in some global data structure. The transactional memory system will define some locking data structure covering each possibly-shared data item. When a location is write-locked, this locking data structure will contain some indication of the transaction that holds the lock. When Tx1 discovers that the data item it seeks to access is write-locked, it can read this locking data structure to discover the identity of the Tx2. The crux of the problem is that once Tx1 had obtained a pointer to the data structure representing Tx2, and prepares to read information from that data structure, Tx2 may complete, and its data structure may be deallocated, and perhaps reallocated for some other purpose. In this situation, the information Tx1 reads about Tx2 is not stable.